package com.web.webstart.business.service.impl;

import com.web.webstart.base.util.DateUtils;
import com.web.webstart.base.util.FileUtil;
import com.web.webstart.base.util.XaDbStatus;
import com.web.webstart.base.util.XaUtil;
import com.web.webstart.base.util.jpa.DynamicSpecifications;
import com.web.webstart.base.util.jpa.SearchFilter;
import com.web.webstart.base.util.jpa.WebUitl;
import com.web.webstart.business.entity.Model;
import com.web.webstart.business.entity.Project;
import com.web.webstart.business.entity.Property;
import com.web.webstart.business.repository.ModelRepository;
import com.web.webstart.business.repository.ProjectRepository;
import com.web.webstart.business.repository.PropertyRepository;
import com.web.webstart.business.service.XaVelocityService;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.io.StringWriter;
import java.net.URL;
import java.util.*;

import static com.web.webstart.base.util.DateUtils.YYYYMMDDHHMMSS;

@Service("XaVelocityServiceImpl" )
public class XaVelocityServiceImpl implements XaVelocityService {

    @Autowired
    private PropertyRepository propertyRepository;

    @Autowired
    private ModelRepository modelRepository;

    @Autowired
    private ProjectRepository projectRepository;

    private VelocityContext context;//http://blog.csdn.net/facekbook/article/details/53102143

    public XaVelocityServiceImpl() {
        Properties prop = new Properties();
        // windows D:/java/apache-tomcat-7.0.70/webapps/ExpressGang/WEB-INF/classes/
        // macos  /mylibs/tomcat/apache-tomcat-7.0.61/lib/
        String templateRootPath = null;
        if (FileUtil.isWindos()) {
            templateRootPath = this.getClass().getResource("/" ).getPath();        //模板的根路径 模板路径
        } else {
            URL url = Thread.currentThread().getContextClassLoader().getResource("/" );
            if (url == null) {
                //junit测试
                templateRootPath = this.getClass().getResource("/" ).getPath();
            } else {
                templateRootPath = url.getPath();
            }
        }
        prop.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, templateRootPath);
        prop.setProperty(Velocity.ENCODING_DEFAULT, "utf-8" );
        prop.setProperty(Velocity.INPUT_ENCODING, "utf-8" );
        prop.setProperty(Velocity.OUTPUT_ENCODING, "utf-8" );
        Velocity.init(prop);
        context = new VelocityContext();
        context.put("velocityDate", DateUtils.getStrDate(new Date(),YYYYMMDDHHMMSS));//注入时间数据数据
    }

    private String initUpperCase(String str) {
        return str.toUpperCase().substring(0, 1) + str.substring(1);
    }

    private String initLowerCase(String str) {
        return str.toLowerCase().substring(0, 1) + str.substring(1);
    }

    @Override
    public Map<String, String> publishModel(Long projectId, Long modelId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            rt.put(initUpperCase(model.getIdentify()) + ".java", templateModel());
        }
        return rt;
    }

    @Override
    public Map<String, String> publishRepository(Long projectId, Long modelId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            rt.put(initUpperCase(model.getIdentify()) + "Repository.java", templateModel());
        }
        return rt;
    }

    @Override
    public Map<String, String> publishService(Long projectId, Long modelId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            rt.put(initUpperCase(model.getIdentify()) + "Service.java", templateService());
        }
        return rt;
    }

    @Override
    public Map<String, String> publishServiceImpl(Long projectId, Long modelId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            rt.put(initUpperCase(model.getIdentify()) + "ServiceImpl.java", templateServiceImpl());
        }
        return rt;
    }

    @Override
    public Map<String, String> publishController(Long projectId, Long modelId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            rt.put(initUpperCase(model.getIdentify()) + "Controller.java", templateController());
        }
        return rt;
    }

    @Override
    public Map<String, String> publishProject(Long projectId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        List<Model> models = modelRepository.findByProjectIdAndStatusNot(projectId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(models)) {
            for (Model model : models) {
                model.setProperties(propertyRepository.findByModelId(model.getId()));
                removePropertyDelStatus(model.getProperties());
                context.put("model", model);
                rt.put(initUpperCase(model.getIdentify()) + ".java", templateModel());
                rt.put(initUpperCase(model.getIdentify()) + "Repository.java", templateRepository());
                rt.put(initUpperCase(model.getIdentify()) + "Service.java", templateService());
                rt.put(initUpperCase(model.getIdentify()) + "ServiceImpl.java", templateServiceImpl());
                rt.put(initUpperCase(model.getIdentify()) + "Controller.java", templateController());
                rt.put(initUpperCase(model.getIdentify()) + "Vo.java", templateVo());
                rt.put(initLowerCase(model.getIdentify()) + "List.jsp", templateHtmlList());
                rt.put(initLowerCase(model.getIdentify()) + "Edit.jsp", templateHtmlEdit());
                //rt.put(initLowerCase(model.getIdentify())+".js", templateJs());
                rt.put("Api" +initUpperCase(model.getIdentify()) + "Controller.java", merge("ApiModelController.vm" ));
                //test的不要
                //rt.put(initUpperCase("Test"+model.getIdentify())+"Service.java", merge("TestModelService.vm"));
                //rt.put(initUpperCase("Test"+model.getIdentify())+"Controller.java", merge("TestModelController.vm"));
                rt.put("Api" +initUpperCase(model.getIdentify()) + "Service.java", templateApiService());
                rt.put("Api" +initUpperCase(model.getIdentify()) + "ServiceImpl.java", templateApiServiceImpl());
            }
        }
        return rt;
    }

    /**
     * @ClassName: publishProjectModel
     * @Description: 同时发布多个模块 发布单个模块
     * @author duandazhi
     * @date 2016/11/8 下午3:54
     */
    @Override
    public Map<String, String> publishProjectModels(Long projectId, String[] modelIds) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }

        Pageable pageable = WebUitl.buildPageRequest(0, Integer.MAX_VALUE, "[{property:'createTime',direction:'DESC'}]" );
        Map<String, SearchFilter> filters = new HashMap<>();
        filters.put("status", new SearchFilter("status", SearchFilter.Operator.NE, XaDbStatus.DB_STATUS_DELETE));
        List ids = Arrays.asList(modelIds);
        filters.put("id", new SearchFilter("id", SearchFilter.Operator.IN, ids));
        Page<Model> page = modelRepository.findAll(DynamicSpecifications
                .bySearchFilter(filters.values(), Model.class), pageable);

        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(page)) {
            for (int i = 0; i < page.getContent().size(); ++i) {
                Model model = page.getContent().get(i);
                model.setProperties(propertyRepository.findByModelId(model.getId()));
                removePropertyDelStatus(model.getProperties());
                context.put("model", model);
                rt.put(initUpperCase(model.getIdentify()) + ".java", templateModel());
                rt.put(initUpperCase(model.getIdentify()) + "Repository.java", templateRepository());
                rt.put(initUpperCase(model.getIdentify()) + "Service.java", templateService());
                rt.put(initUpperCase(model.getIdentify()) + "ServiceImpl.java", templateServiceImpl());
                rt.put(initUpperCase(model.getIdentify()) + "Controller.java", templateController());
                rt.put(initUpperCase(model.getIdentify()) + "Vo.java", templateVo());
                rt.put(initLowerCase(model.getIdentify()) + "List.jsp", templateHtmlList());
                rt.put(initLowerCase(model.getIdentify()) + "Edit.jsp", templateHtmlEdit());
                //rt.put(initLowerCase(model.getIdentify())+".js", templateJs());
                rt.put("Api" +initUpperCase(model.getIdentify()) + "Controller.java", merge("ApiModelController.vm" ));
                rt.put("Test" +initUpperCase(model.getIdentify()) + "Service.java", merge("TestModelService.vm" ));
                rt.put("Test" +initUpperCase(model.getIdentify()) + "Controller.java", merge("TestModelController.vm" ));
                rt.put("Api" +initUpperCase(model.getIdentify()) + "Service.java", templateApiService());
                rt.put("Api" +initUpperCase(model.getIdentify()) + "ServiceImpl.java", templateApiServiceImpl());
            }
        }
        return rt;
    }

    private void removePropertyDelStatus(List<Property> properties) {
        Iterator<Property> iter = properties.iterator();
        while (iter.hasNext()) {
            Property property = iter.next();
            if (XaDbStatus.DB_STATUS_DELETE == property.getStatus()) {
                iter.remove();
            }
        }
    }

    @Override
    public Map<String, String> publishVo(Long projectId, Long modelId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            rt.put(initUpperCase(model.getIdentify()) + "Vo.java", templateVo());
        }
        return rt;
    }

    private String merge(String templateFile) {
        Template template = null;
        try {
            template = Velocity.getTemplate(templateFile);
        } catch (ResourceNotFoundException rnfe) {
            System.err.println(rnfe.getMessage());
        } catch (ParseErrorException pee) {
            System.err.println(pee.getMessage());
        } catch (MethodInvocationException mie) {
            System.err.println(mie.getMessage());
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        StringWriter sw = new StringWriter();

        template.merge(context, sw);
        return sw.getBuffer().toString();
    }

    public String templateModel() {
        return merge("Model.vm" );
    }

    public String templateRepository() {
        return merge("ModelRepository.vm" );
    }

    public String templateService() {
        return merge("ModelService.vm" );
    }

    public String templateServiceImpl() {
        return merge("ModelServiceImpl.vm" );
    }

    public String templateController() {
        return merge("ModelController.vm" );
    }

    public String templateVo() {
        return merge("ModelVo.vm" );
    }

    public String templateHtmlList() {
        return merge("ModelList.vm" );
    }

    public String templateHtmlEdit() {
        return merge("ModelEdit.vm" );
    }

//	public String templateJs(){
//		return merge("modelJs.vm");
//	}

    public String templateApiService() {
        return merge("ApiModelService.vm" );
    }

    public String templateApiServiceImpl() {
        return merge("ApiModelServiceImpl.vm" );
    }

    private Map<String, String> changeSet(Long projectId, Long modelId, String sName, String eName, String type) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            String content = "";
            if (type.equals("Api" )) {
                content = merge("ApiModelController.vm" );
            } else if (type.equals("TestService" )) {
                content = merge("TestModelService.vm" );
            } else if (type.equals("TestController" )) {
                content = merge("TestModelController.vm" );
            }
            rt.put(sName + initUpperCase(model.getIdentify()) + eName + ".java", content);
        }
        return rt;
    }

    @Override
    public Map<String, String> publishApi(Long projectId, Long modelId) {
        return changeSet(projectId, modelId, "Api", "Controller", "Api" );
    }

    @Override
    public Map<String, String> publishTestService(Long projectId, Long modelId) {
        return changeSet(projectId, modelId, "Test", "Service", "TestService" );
    }

    @Override
    public Map<String, String> publishTestController(Long projectId,
                                                     Long modelId) {
        return changeSet(projectId, modelId, "Test", "Controller", "TestController" );
    }

    @Override
    public Map<String, String> publishTestProject(Long projectId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        List<Model> models = modelRepository.findByProjectIdAndStatusNot(projectId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(models)) {
            for (Model model : models) {
                model.setProperties(propertyRepository.findByModelId(model.getId()));
                removePropertyDelStatus(model.getProperties());
                context.put("model", model);
                rt.put("Test" + initUpperCase(model.getIdentify()) + "Service.java", merge("TestModelService.vm" ));
                rt.put("Test" + initUpperCase(model.getIdentify()) + "Controller.java", merge("TestModelController.vm" ));
            }
        }
        return rt;
    }

    @Override
    public Map<String, String> publishApiService(Long projectId, Long modelId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            rt.put("Api" + initUpperCase(model.getIdentify()) + "Service.java", templateService());
        }
        return rt;
    }

    @Override
    public Map<String, String> publishApiServiceImpl(Long projectId,
                                                     Long modelId) {
        Project project = projectRepository.findByIdAndStatusNot(projectId, XaDbStatus.DB_STATUS_DELETE);
        if (XaUtil.isNotEmpty(project)) {
            context.put("project", project);
        } else {
            return null;
        }
        Model model = modelRepository.findByIdAndStatusNot(modelId,
                XaDbStatus.DB_STATUS_DELETE);
        Map<String, String> rt = new HashMap<String, String>();
        if (XaUtil.isNotEmpty(model)) {
            context.put("model", model);
            rt.put("Api" + initUpperCase(model.getIdentify()) + "ServiceImpl.java", templateServiceImpl());
        }
        return rt;
    }


}
